

/**
* @file dispatcher.c
*
* @brief Example embedded dispatcher implementation
*
* The following code is not meant to be a complete and definitive API library. Rather,
* it is meant to show how the communication with the SIO Processor COULD be handled,
* with example message construction and response parsing.
*
* In this code, a try-catch-throw mechanism is used for exception handling.
* An open source try-catch-throw implementation, cexcept, can be found here:
*
* http://www.nicemice.net/cexcept/
*
* Copyright ?2011 HID Corporation.	All rights reserved.
*
* This software is protected by copyright law and international treaties.
* Any unauthorized reproduction, distribution or use of the software is prohibited.
*
*/
#include <stdio.h>
#include <string.h>
#include <stddef.h>

#include "exception.h"
#include "ipcMessage.h"
#include "utils.h"
#include "asn1.h"
#include "tlv.h"
#include "dispatcher.h"
#include "Debug.h"
#include "HIDRF.h"
#include "SIM.h"
#include "BSP_Delay.h"
#include "WatchDog.h"
#include "Log.h"

#ifdef NDEBUG
#define D_DISPACH	0
#else
#define D_DISPACH	1
#endif

#if D_DISPACH
#define DBG_DISP_WRITE(str)					DBG_WRITE(str)
#define DBG_DISP_PRINT_BUF(fmt, buf, len)		Log_Buf(0, RTT_CTRL_TEXT_BRIGHT_WHITE, buf, len, 200, fmt)
#else
#define DBG_DISP_WRITE(str)					
#define DBG_DISP_PRINT_BUF(fmt, buf, len)		
#endif

#define SOURCE_NODE IPC_NODEID_EXTERNAL_APP // SAM is expecting this command to come from an ext.app.
#define DEST_NODE IPC_NODEID_SAM
#define REPLY_NODE IPC_NODEID_EXTERNAL_APP
#define PRIORITY MAX_PRIORITY
#define TIMEOUT 0x00, 0x00

#define IC_RX_BUFF_LEN 128	//if rx buffer is less than max card response T=1 layer may chain with CCID
#define IC_WK_BUFF_LEN 128 //

#define LOOP_LIMIT 150 //limit imposed to prevent error lock out

#define MAX_PAYLOAD_LENGTH 256


uint8_t workBuffer[256*2];
uint8_t transferBuffer[256*2];
uint8_t rxBuffer[256*2];

static const uint8_t headerCommandForSam[] =
{
	SOURCE_NODE, DEST_NODE, REPLY_NODE, PRIORITY, TIMEOUT
};


// Local function definitions
uint8_t sam_xfrBlock(uint8_t * txData, uint16_t txDataLen, uint8_t * rApdu, uint16_t * rApduLen);
uint8_t hfCommand_process(uint8_t * ptr, uint16_t * payloadLength, uint16_t * availableLength);
void hfResponse_rx(uint8_t * ptr, uint16_t * availableLength, uint8_t * rxData, uint16_t rxDataLen,
	 uint16_t rxRfStatus);
void hfResponse_acknowledge(uint8_t * ptr, uint16_t * availableLength);

void bleResponse_rx(uint8_t * ptr, uint16_t * availableLength, uint8_t * rxData, uint16_t rxDataLen);


uint16_t hfCommand_handle(uint8_t * cmd_send, uint8_t cmdlen, uint8_t * rcv, uint8_t * rcvlen, uint8_t TimerControl,
	 uint8_t TimerReload);
void samCommand_getVersionInfo(uint8_t * ptr, uint16_t * availableLength);

void samCommand_getContentElement(uint8_t * ptr, uint16_t * availableLength);

T_BOOL t1_xfrBlock(uint8_t * cApdu, uint16_t cApduLen, uint8_t * rApdu, uint16_t * rApduLen)
{
	DBG_DISP_PRINT_BUF("To SE>", cApdu, cApduLen);
	
	int ret = SIM_Exchange(cApdu, cApduLen, rApdu, rApduLen);
	if(ret){
		DBG_DISP_WRITE("SE rx fail!");
		return _FALSE;
	}
	DBG_DISP_PRINT_BUF("From SE<", rApdu, *rApduLen);
	return _TRUE;
}


void rf_setField(RfStates rfstates)
{
	HID_RF_SetFeild(rfstates);
#if 0
	if (rfstates) {
		Sic9310_Powerup();
	}
	else {
		Sic9310_Powerdown();
	}
#endif 
}


uint16_t hfCommand_handle(uint8_t * cmd_send, uint8_t cmdlen, uint8_t * rcv, uint8_t * rcvlen, uint8_t TimerControl,
	 uint8_t TimerReload)
{
#if 0
	unsigned char * cmd_save = cmd_send;
	char status;
	uint16_t hf_staus;

	status = HID_Transceive((unsigned char *) cmd_save, (unsigned char) cmdlen, rcv, rcvlen, TimerControl,
		 TimerReload);

	if (status == TI_OK)
		hf_staus = ASN1_RF_STATUS_OK;
	else if (status == I2_FRAMINGERR)
		hf_staus = ASN1_RF_STATUS_FRAMING_ERROR;
	else if (status == I2_CRCERR)
		hf_staus = ASN1_RF_STATUS_CRC_ERROR;
	else if (status == I2_COLLERR)
		hf_staus = ASN1_RF_STATUS_COLLISION;
	else 
		hf_staus = ASN1_RF_STATUS_TIMEOUT;


	return hf_staus;
#endif 
	return 0;
}


#if 0
void samCommand_read(uint8_t * ptr, uint16_t * availableLength)
{
	uint8_t * command_lengthPosition;

	// set header
	tlv_setData((uint8_t *) headerCommandForSam, sizeof(headerCommandForSam), &ptr, availableLength);

	// create sam command
	command_lengthPosition = asn1_openTag1(ASN1_CHOICE_CONSTRUCTED | PAYLOAD_SAM_COMMAND, &ptr, availableLength);
	{
		uint8_t * samCommand_lengthPosition;

		// create get-content-element command
		samCommand_lengthPosition = asn1_openTag1(ASN1_CHOICE_CONSTRUCTED | SAMCOMMAND_PROCESS_CARD_API, &ptr,
			 availableLength);
		{
			uint8_t * samCommand_lengthPosition1;

			samCommand_lengthPosition1 = asn1_openTag1(ASN1_CHOICE_CONSTRUCTED | 1, &ptr, availableLength);
			{
				uint8_t elementTag = 1;

				// set contentElementTag
				tlv_create(ASN1_CLASS_CONTEXT | 0, &elementTag, sizeof(elementTag), &ptr, availableLength);
			}
			asn1_closeTag1(samCommand_lengthPosition1, ptr);
		}
		asn1_closeTag1(samCommand_lengthPosition, ptr);
	}
	asn1_closeTag1(command_lengthPosition, ptr);
}
#endif 


#if 0
int HID_APDU(uint8_t * payload, uint16_t * payloadLen, uint8_t * send, uint8_t len)
{
	uint8_t rxBuffer[IC_RX_BUFF_LEN];
	uint8_t rxLength;
	uint8_t * ptr;
	uint16_t availableLength;
	uint16_t payloadLength;
	uint16_t loopLimit = 0;
	int ret;
	
	ptr = workBuffer;
	availableLength = sizeof(workBuffer);
	samCommand_read(ptr, &availableLength);

	while (1) {

		if (loopLimit++ > LOOP_LIMIT) {
			return(TIMEOUT_ERROR);
		}

		rxLength = 0;

		if (loopLimit == 1) {
			DBG_DISP_WRITE("> ");DBG_DISP_PRINT_BUF(send, len);DBG_DISP_WRITE("\n");
			ret = sam_xfrBlock(send, len, rxBuffer, &rxLength);
			DBG_DISP_WRITE("< ");DBG_DISP_PRINT_BUF(rxBuffer, rxLength);DBG_DISP_WRITE("\n");
		}
		else {
			DBG_DISP_WRITE("> ");DBG_DISP_PRINT_BUF(workBuffer, (sizeof(workBuffer) - availableLength));DBG_DISP_WRITE("\n");
			ret = sam_xfrBlock(workBuffer, (sizeof(workBuffer) - availableLength), rxBuffer, &rxLength);
			DBG_DISP_WRITE("< ");DBG_DISP_PRINT_BUF(rxBuffer, rxLength);DBG_DISP_WRITE("\n");
		}
		if(ret != _TRUE){
			DBG_DISP_WRITE("sam error\r\n");
			return SIO_ERROR;
		}
		if(rxLength <= sizeof(IPC_HEADER)){
			DBG_DISP_WRITE("TLV_INVALID_SETLENGTH\r\n");
			DBG_DISP_WRITE("rx len=");DBG_DISP_PRINT_BUF(&rxLength, 1);DBG_DISP_WRITE("\r\n");
			return TLV_INVALID_SETLENGTH;
		}
		if ((rxLength - sizeof(IPC_HEADER)) > (1 << 8)) { //check received length fits in uint16_t
			DBG_DISP_WRITE("TLV_INSUFFICIENT_BUFFER\r\n");
			DBG_DISP_WRITE("rx len=");DBG_DISP_PRINT_BUF(&rxLength, 1);DBG_DISP_WRITE("\r\n");
			return(TLV_INSUFFICIENT_BUFFER);
		}
		else {
			payloadLength = rxLength - sizeof(IPC_HEADER);
		}
		ptr = rxBuffer + sizeof(IPC_HEADER);		// point to start of payload
		switch (rxBuffer[1])
		{ // destination node is 2nd byte of header
			case IPC_NODEID_EXTERNAL_APP:
				{
					if (tlv_readTag(&ptr, &payloadLength) == (ASN1_CHOICE_CONSTRUCTED | PAYLOAD_RESPONSE)) {

						uint8_t tag;

						tlv_readLength(&ptr, &payloadLength);
						tag = tlv_readTag(&ptr, &payloadLength);
						tlv_readLength(&ptr, &payloadLength);
						payloadLength = * (ptr - 1);

						if ((tag == (ASN1_CHOICE_PRIMITIVE | RESPONSE_SAMCOMMAND)) /* && (payloadLength >= 0)*/) //&& (payloadLength <= *payloadLen)) 
						{
							utils_copy(ptr, payload, payloadLength);
							*payloadLen = payloadLength;

							DBG_DISP_WRITE("GET_ELMENT_OK\r\n");
							//return (GET_ELMENT_OK);
							return(1);
						}
						else {
							DBG_DISP_WRITE("GET_ELMENT_FAIL\r\n");
							return(0);
						}
					}
					break;
				}

			case IPC_NODEID_HF_INTERFACE:
				ret = hfCommand_process(ptr, &payloadLength, &availableLength);
				if(ret != _TRUE){
					DBG_DISP_WRITE("IPC_NODEID_HF_INTERFACE ERROR!\r\n");
					return ret;
				}
				break;

			default:				
				DBG_DISP_WRITE("PICOREAD_FIELD_ERROR\r\n");
				return(PICOREAD_FIELD_ERROR);
//				break;
		}
	}

	return (1);
}
#endif 



static int api_DoDispatch(uint8_t * cbuf, uint8_t clen, uint8_t * rbuf, uint8_t * rlen)
{
//		uint8_t rxBuffer[IC_RX_BUFF_LEN];
	uint16_t rxLength;
	uint8_t * ptr;
	uint16_t availableLength;
	uint16_t payloadLength;
	uint16_t loopLimit = 0;
	int ret;
	
	ptr = workBuffer;
	availableLength = sizeof(workBuffer);
	memcpy(ptr, headerCommandForSam, sizeof(headerCommandForSam));
	ptr += sizeof(headerCommandForSam);
	memcpy(ptr, cbuf, clen);
	availableLength -= (clen + sizeof(headerCommandForSam));
	
	while (loopLimit < LOOP_LIMIT) {
		++loopLimit;
		rxLength = 0;

//			LOG_B(">", workBuffer, (sizeof(workBuffer) - availableLength), 16);
		ret = sam_xfrBlock(workBuffer, (sizeof(workBuffer) - availableLength), rxBuffer, &rxLength);
//			LOG_B("<", rxBuffer, rxLength, 16);

		if(ret != _TRUE){
			LOG_E("sam error");
			return SIO_ERROR;
		}
		if(rxLength <= sizeof(IPC_HEADER)){
			LOG_E("sam error, rxLength = %d", rxLength);
			return TLV_INVALID_SETLENGTH;
		}
		if ((rxLength - sizeof(IPC_HEADER)) > (1 << 8)) { //check received length fits in uint16_t
			LOG_E("TLV_INSUFFICIENT_BUFFER, rxLength = %d", rxLength);
			return(TLV_INSUFFICIENT_BUFFER);
		}
		else {
			payloadLength = rxLength - sizeof(IPC_HEADER);
		}
		ptr = rxBuffer + sizeof(IPC_HEADER);		// point to start of rbuf
		switch (rxBuffer[1])
		{ // destination node is 2nd byte of header
			case IPC_NODEID_EXTERNAL_APP:
				{
					if (tlv_readTag(&ptr, &payloadLength) == (ASN1_CHOICE_CONSTRUCTED | PAYLOAD_RESPONSE)) {

						uint8_t tag;

						tlv_readLength(&ptr, &payloadLength);
						tag = tlv_readTag(&ptr, &payloadLength);
						tlv_readLength(&ptr, &payloadLength);
						payloadLength = * (ptr - 1);

						if (tag == (ASN1_CHOICE_PRIMITIVE | RESPONSE_SAMCOMMAND))
						{
							utils_copy(ptr, rbuf, payloadLength);
							*rlen = payloadLength;
							LOG_D("GET_ELMENT_OK");
							return 0;
						}
						else {
							LOG_E("GET_ELMENT_FAIL");
							return TLV_NOT_FOUND;
						}
					}
					else{
						LOG_E("GET_ELMENT_FAIL");
						return TLV_NOT_FOUND;
					}
//						break;
				}

			case IPC_NODEID_HF_INTERFACE:
				ret = hfCommand_process(ptr, &payloadLength, &availableLength);
				if(ret != _TRUE){
					LOG_E("IPC_NODEID_HF_INTERFACE ERROR!\r\n");
					return ret;
				}
				break;

			default:				
				LOG_E("PICOREAD_FIELD_ERROR\r\n");
				return(PICOREAD_FIELD_ERROR);
		}
	}

	return(TIMEOUT_ERROR);
}

int api_Dispatch(uint8_t * cbuf, uint8_t clen, uint8_t * rbuf, uint8_t * rlen)
{
	int e;
	
	Try {
		return api_DoDispatch(cbuf, clen, rbuf, rlen);
	}
	Catch(e){
		LOG_E("exception: %d", e);
	}
	
	return e;
}


static int api_DoDispatch2(uint8_t wdt, uint8_t * cbuf, uint16_t clen, uint8_t ** rbuf, uint16_t * rlen)
{
	uint16_t rxLength;
	uint8_t * ptr;
	uint16_t availableLength;
	uint16_t payloadLength;
	uint16_t loopLimit = 0;
	int ret;
	ptr = workBuffer;
	availableLength = sizeof(workBuffer);
	memcpy(ptr, headerCommandForSam, sizeof(headerCommandForSam));
	ptr += sizeof(headerCommandForSam);
	memcpy(ptr, cbuf, clen);
	availableLength -= (clen + sizeof(headerCommandForSam));
	
	while (loopLimit < LOOP_LIMIT) {
		++loopLimit;
		rxLength = 0;
		if(wdt){
			WatchDog_Reset();
		}
		ret = sam_xfrBlock(workBuffer, (sizeof(workBuffer) - availableLength), rxBuffer, &rxLength);

		if(ret != _TRUE){
			LOG_E("sam error");
			return SIO_ERROR;
		}
		if(rxLength <= sizeof(IPC_HEADER)){
			LOG_E("sam error, rxLength = %d", rxLength);
			return TLV_INVALID_SETLENGTH;
		}
		if ((rxLength - sizeof(IPC_HEADER)) > (1 << 8)) { //check received length fits in uint16_t
			LOG_E("TLV_INSUFFICIENT_BUFFER, rxLength = %d", rxLength);
			return(TLV_INSUFFICIENT_BUFFER);
		}
		else {
			payloadLength = rxLength - sizeof(IPC_HEADER);
		}
		ptr = rxBuffer + sizeof(IPC_HEADER);		// point to start of rbuf
		switch (rxBuffer[1])
		{ // destination node is 2nd byte of header
			case IPC_NODEID_EXTERNAL_APP:
				{
					if (tlv_readTag(&ptr, &payloadLength) == (ASN1_CHOICE_CONSTRUCTED | PAYLOAD_RESPONSE)) {
						tlv_readLength(&ptr, &payloadLength);
						*rbuf = ptr;
						*rlen = payloadLength;
						return 0;
					}
					else{
						uint16_t dlen;
						tlv_readLength(&ptr, &payloadLength);
						dlen = tlv_search(&ptr, &payloadLength, 0x80);
						ret = *ptr;
						LOG_E("fail: %d", ret);
						if(ret){
							return ret;
						}
						return TLV_NOT_FOUND;
					}
				}

			case IPC_NODEID_HF_INTERFACE:
				ret = hfCommand_process(ptr, &payloadLength, &availableLength);
				if(ret != _TRUE){
					LOG_E("IPC_NODEID_HF_INTERFACE ERROR!\r\n");
					return ret;
				}
				break;
			case IPC_NODEID_BLE:{

			}
			break;
			case IPC_NODEID_I2C:{

			}
			break;
			default:				
				LOG_E("PICOREAD_FIELD_ERROR\r\n");
				return(PICOREAD_FIELD_ERROR);
		}
	}

	return(TIMEOUT_ERROR);
}

int api_Dispatch2(uint8_t wdt, uint8_t * cbuf, uint16_t clen, uint8_t ** rbuf, uint16_t * rlen)
{
	int e;
	
	Try {
		return api_DoDispatch2(wdt, cbuf, clen, rbuf, rlen);
	}
	Catch(e){
		LOG_E("exception: %d", e);
	}
	
	return e;
}



#if 1
/// <summary>
/// API function to get the VersionInfo from the SAM
/// </summary>
/// <param name="version">buffer to hold the retrieved VersionInfo</param>
/// <param name="versionLen">maximum and returned length of the VersionInfo</param>
/// <returns>TRUE if command is successful, FALSE otherwise</returns>
int api_getVersionInfo(uint8_t * version, uint16_t * versionLen)
{
	//uint8_t res;
	uint8_t rxBuffer[IC_RX_BUFF_LEN];

	//uint32_t rxLength;
	uint16_t rxLength = 0;
	uint8_t * ptr;
	uint16_t availableLength;
	uint16_t payloadLength;
	uint16_t loopLimit = 0;



	ptr = workBuffer;
	availableLength = sizeof(workBuffer);

	//Try	{
	samCommand_getVersionInfo(ptr, &availableLength);

	while (1) {

		if (loopLimit++ > LOOP_LIMIT) {
			return(TIMEOUT_ERROR);
		}

		rxLength = 0;
		sam_xfrBlock(workBuffer, (sizeof(workBuffer) -availableLength), rxBuffer, &rxLength);

		ptr = rxBuffer + sizeof(IPC_HEADER);		// point to start of payload

		if (rxLength > 255) {
			return(TLV_INSUFFICIENT_BUFFER);
		}
		else {
			payloadLength = rxLength - sizeof(IPC_HEADER);
		}

		// destination node is 2nd byte of header
		switch (rxBuffer[1])
		{ 
			case IPC_NODEID_EXTERNAL_APP:
				{
					// read initial tag: expected tag 'response' //
					if (tlv_readTag(&ptr, &payloadLength) == (ASN1_CHOICE_CONSTRUCTED | PAYLOAD_RESPONSE)) {
						uint8_t tag;
						uint16_t len;

						tlv_readLength(&ptr, &payloadLength);

						// read response tag: expected tag 'sam response' //
						tag = tlv_readTag(&ptr, &payloadLength);
						len = tlv_readLength(&ptr, &payloadLength);

						if (tag == (ASN1_CHOICE_PRIMITIVE | RESPONSE_SAMCOMMAND)) {
							utils_copy(ptr, version, len);
							*versionLen = len;
							return(0);
						}
						else {
							return(TLV_MALFORMED);
						}
					}
					break;
				}

			default:
				return(NOT_SUPPORTED);
		}
	}

}

#endif 

#if 0
T_BOOL api_AntiCollision1(uint8_t * csn)
{
	uint8_t CMD_status;

	csn[0] = 0x0a;
	CMD_status = HID_Transceive(csn, 1, &csn[1], csn, 0x06, 0x4a);

	if (CMD_status == I2_FRAMINGERR) {
		csn[0] = 0x0c;
		CMD_status = HID_Transceive(csn, 1, &csn[1], csn, 0x06, 0x4a);

		if (CMD_status == TI_OK) {
			csn[0] = 0x81;
			CMD_status = HID_Transceive(csn, 9, &csn[1], csn, 0x06, 0x4a);

			if (CMD_status == TI_OK) {
				return (_TRUE);
			}
		}
	}
	return (_FALSE);
}
#endif 

#if 0
int IClass_AntiCollision(uint8_t * csn, uint8_t* clen)
{
	uint8_t status;
	uint8_t buf[40];
	uint8_t rlen = 0;
	
	buf[0] = 0x0a;
	status = HID_Transceive(buf, 1, &buf[1], buf, 0x06, 0x4a);

	if (status != I2_FRAMINGERR) {
		return -1;
	}
	BSP_Delay_ms(1);
	buf[0] = 0x0c;
	status = HID_Transceive(buf, 1, &buf[1], buf, 0x06, 0x4a);
	
	if (status != TI_OK) {	
		return -2;
	}
	BSP_Delay_ms(1);
	buf[0] = 0x81;
	status = HID_Transceive(buf, 9, &buf[1], &rlen, 0x06, 0x4a);
	if(status != TI_OK){
		return -3;
	}

	if(csn){
		memcpy(csn, &buf[1], rlen);
	}
	
	if(rlen){
		*clen = rlen;
	}
	
	return 0;
}

#endif 


/// <summary>
/// Prepare a samCommand_getContentElement message
/// </summary>
/// <param name="ptr">buffer to hold the message data</param>
/// <param name="availableLength">free space remaining in the buffer</param>
void samCommand_AntiCollision(uint8_t * ptr, uint16_t * availableLength)
{
	uint8_t * command_lengthPosition;

	// set header
	tlv_setData((uint8_t *) headerCommandForSam, sizeof(headerCommandForSam), &ptr, availableLength);

	// create sam command
	command_lengthPosition = asn1_openTag1(ASN1_CHOICE_CONSTRUCTED | PAYLOAD_SAM_COMMAND, &ptr, availableLength);
	{
		uint8_t * samCommand_lengthPosition;

		// create get-content-element command
		samCommand_lengthPosition = asn1_openTag1(ASN1_CHOICE_CONSTRUCTED | SAMCOMMAND_ANTI_COLLISION, &ptr,
			 availableLength);
		{
			//uint16_t anticollisionTag = 0x0405;
			uint16_t anticollisionTag = 0x0504;

			// set contentElementTag
			tlv_create(ASN1_CLASS_CONTEXT | 0, (uint8_t *) &anticollisionTag, sizeof(anticollisionTag), &ptr,
				 availableLength);
		}
		asn1_closeTag1(samCommand_lengthPosition, ptr);
	}
	asn1_closeTag1(command_lengthPosition, ptr);
}


#if 0
int api_AntiCollision(uint8_t * csn)
{
	uint8_t rxLength;
	uint8_t * ptr;

	//uint8_t* ptr_save;
	uint16_t availableLength;
	uint16_t payloadLength;
	uint16_t loopLimit = 0;

	//uint8_t tag,len,len_save,timecotol,timereload;
//		nad_pcb_len.nad = 0;
//		nad_pcb_len.pcb = 0;
//		nad_pcb_len.pcb ^= 0x40;
	ptr = workBuffer;
	availableLength = sizeof(workBuffer);

	//Try	{
	samCommand_AntiCollision(ptr, &availableLength);


	while (1) {

		if (loopLimit++ > LOOP_LIMIT) {
			return(TIMEOUT_ERROR);
		}
//			WatchDog_Reset();

//			rxLength = sizeof(rxBuffer);				//44 0A 44 00 00 00 A0 82 00 08 B7 82 00 04 80 02 04 05  //18
		rxLength = 0;				//44 0A 44 00 00 00 A0 82 00 08 B7 82 00 04 80 02 04 05  //18

		// 44 0A 44 00 00 00 A0 82 00 08 B7 82 00 04 80 02 04 05
		// 44 0A 44 00 00 00 A0 82 00 06 B7 	  04 80 02 04 05  //16
		//			
		sam_xfrBlock(workBuffer, (sizeof(workBuffer) -availableLength), rxBuffer, &rxLength);

		//	return (_TRUE);  //0A 14 0A 00 00 00 A1 0D A1 0B 80 01 0A 81 02 00 04 82 02 06 4A
		// 0A 14 0A 00 00 00 A1 0D A1 0B 80 01 0A 81 02 00 04 82 02 06 4A
		//	UART_Senddata(rxLength,rxBuffer);
		ptr = rxBuffer + sizeof(IPC_HEADER);		// point to start of payload

		if ((rxLength - sizeof(IPC_HEADER)) > (1 << 8)) { //check received length fits in uint16_t
			return(TLV_INSUFFICIENT_BUFFER);
		}
		else {
			payloadLength = rxLength - sizeof(IPC_HEADER);
		}

		switch (rxBuffer[1])
		{ // destination node is 2nd byte of header
			case IPC_NODEID_EXTERNAL_APP: //0x44
				{
					if (tlv_readTag(&ptr, &payloadLength) == (ASN1_CHOICE_CONSTRUCTED | PAYLOAD_RESPONSE)) {

						uint8_t tag, len;

						tlv_readLength(&ptr, &payloadLength);
						tag = tlv_readTag(&ptr, &payloadLength);
						tlv_readLength(&ptr, &payloadLength);
						tag = tlv_readTag(&ptr, &payloadLength);
						len = tlv_readLength(&ptr, &payloadLength);
						ptr += len;
						tag = tlv_readTag(&ptr, &payloadLength);
						len = tlv_readLength(&ptr, &payloadLength);

						utils_copy(ptr, &csn[1], len);
						csn[0] = len;
						tag = tag; //消除警告
						return (_TRUE);

					}
					break;
				}

			case IPC_NODEID_HF_INTERFACE: //0x14
				hfCommand_process(ptr, &payloadLength, &availableLength);
				break;

			default:
				return (_FALSE);

//					break;
		}
	}


//		return (_FALSE);
}
#endif 




#if 0

/// <summary>
/// API function to get the ContentElement of a detected card via the SAM
/// </summary>
/// <param name="payload">buffer to hold the retrieved ContentElement</param>
/// <param name="payloadLen">maximum and returned length of the ContentElement</param>
/// <returns>TRUE if command is successful, FALSE otherwise</returns>
int api_getContentElement(uint8_t * payload, uint16_t * payloadLen)
{
	//uint8_t res;
	uint8_t rxBuffer[IC_RX_BUFF_LEN];

	//uint32_t rxLength;
	uint8_t rxLength;
	uint8_t * ptr;

	//uint8_t* ptr_save;
	uint16_t availableLength;
	uint16_t payloadLength;
	uint16_t loopLimit = 0;

	//uint8_t tag,len,len_save,timecotol,timereload;
//		nad_pcb_len.nad = 0;
//		nad_pcb_len.pcb = 0;
//		nad_pcb_len.pcb ^= 0x40;

	ptr = workBuffer;
	availableLength = sizeof(workBuffer);
	memset(workBuffer, 0, sizeof(workBuffer));
	//Try	{
	samCommand_getContentElement(ptr, &availableLength);

	while (1) {

		if (loopLimit++ > LOOP_LIMIT) {
			return(TIMEOUT_ERROR);
		}

		rxLength = (uint8_t)sizeof(rxBuffer);

		sam_xfrBlock(workBuffer, (sizeof(workBuffer) -availableLength), rxBuffer, &rxLength);

		//	 44 0A 44 00 00 00 A0 82 00 07 A1 82 00 03 80 01 04   //17
		//Command: 44 0A 44 00 00 00 A0 	  05 A1 03		 80 01 04  //13
		ptr = rxBuffer + sizeof(IPC_HEADER);		// point to start of payload

		if ((rxLength - sizeof(IPC_HEADER)) > (1 << 8)) { //check received length fits in uint16_t
			return(TLV_INSUFFICIENT_BUFFER);
		}
		else {
			payloadLength = rxLength - sizeof(IPC_HEADER);
		}

		switch (rxBuffer[1])
		{ // destination node is 2nd byte of header
			case IPC_NODEID_EXTERNAL_APP:
				{
					if (tlv_readTag(&ptr, &payloadLength) == (ASN1_CHOICE_CONSTRUCTED | PAYLOAD_RESPONSE)) {

						uint8_t tag;

						tlv_readLength(&ptr, &payloadLength);
						tag = tlv_readTag(&ptr, &payloadLength);
						tlv_readLength(&ptr, &payloadLength);
						payloadLength = * (ptr - 1);

						if ((tag == (ASN1_CHOICE_PRIMITIVE | RESPONSE_SAMCOMMAND)) && (payloadLength > 0)) //&& (payloadLength <= *payloadLen)) 
						{
							utils_copy(ptr, payload, payloadLength);
							*payloadLen = payloadLength;
							return(GET_ELMENT_OK);
						}
						else {
							return(TLV_MALFORMED);
						}
					}
					break;
				}

			case IPC_NODEID_HF_INTERFACE:
				hfCommand_process(ptr, &payloadLength, &availableLength);
				break;

			default:
				return(NOT_SUPPORTED);
//					break;
		}
	}

//		return (_TRUE);
}

#endif 

/// <summary>
/// Transfer a data block to the SAM and receive the response
///
/// The txData block is wrapped in a T=1 APDU structure before being passed to a
/// low-level callback function which must handle the actual T=1 transmission.
/// </summary>
/// <param name="txData">buffer holding the message data</param>
/// <param name="txDataLen">length of message data in the buffer</param>
/// <param name="rApdu">buffer to hold the received t=1 apdu</param>
/// <param name="rApduLen">length of the received t=1 apdu</param>
uint8_t sam_xfrBlock(uint8_t * txData, uint16_t txDataLen, uint8_t * rApdu, uint16_t * rApduLen)
{
//		static uint8_t cApdu[MAX_PAYLOAD_LENGTH*2];
	uint8_t *cApdu = transferBuffer;
	uint16_t cApduLen;
	uint8_t headerPutData[4];

	headerPutData[0] = SAM_CLA;
	headerPutData[1] = SAM_INS;
	headerPutData[2] = SAM_P1;
	headerPutData[3] = SAM_P2;



	// prepare complete apdu
	cApduLen = sizeof(headerPutData) +P3_LENGTH + txDataLen + LE_LENGTH;

	// set the APDU header in place
	utils_copy(headerPutData, cApdu, sizeof(headerPutData));

	// set extended length P3
	cApdu[sizeof(headerPutData)] = 0;
	utils_setU2(&cApdu[sizeof(headerPutData) + 1], txDataLen);

	// set the message payload as APDU payload
	utils_copy(txData, &cApdu[sizeof(headerPutData) +P3_LENGTH], txDataLen);

	// set extended length Le
	utils_setU2(&cApdu[sizeof(headerPutData) +P3_LENGTH + txDataLen], 0x00);

	if (t1_xfrBlock(cApdu, cApduLen, rApdu, rApduLen) != _TRUE) {
		return(COMM_ERROR);
	}

	if ((rApdu[* ((uint8_t *) rApduLen) - 2] != 0x90) || (rApdu[* ((uint8_t *) rApduLen) - 1] != 0x00)) {
		//SAM response not 0x9000
		return(COMM_ERROR);
	}
	else {
		*rApduLen = (*rApduLen) - 2;				//remove 9000 from rx data

	}
	return _TRUE;
}


#if 1
/// <summary>
/// Prepare a samCommand_getVersionInfo message
/// </summary>
/// <param name="ptr">buffer to hold the message data</param>
/// <param name="availableLength">free space remaining in the buffer</param>
void samCommand_getVersionInfo(uint8_t * ptr, uint16_t * availableLength)
{
	uint8_t * command_lengthPosition;

	// set header
	tlv_setData((uint8_t *) headerCommandForSam, sizeof(headerCommandForSam), &ptr, availableLength);

	// create sam command
	command_lengthPosition = asn1_openTag1(ASN1_CHOICE_CONSTRUCTED | PAYLOAD_SAM_COMMAND, &ptr, availableLength);
	{
		// set get-version command
		tlv_create(ASN1_CHOICE_PRIMITIVE | SAMCOMMAND_GET_VERSION_INFO, NULL, 0, &ptr, availableLength);
	}
	asn1_closeTag1(command_lengthPosition, ptr);
}
#endif 


#if 1
/// <summary>
/// Prepare a samCommand_getContentElement message
/// </summary>
/// <param name="ptr">buffer to hold the message data</param>
/// <param name="availableLength">free space remaining in the buffer</param>
void samCommand_getContentElement(uint8_t * ptr, uint16_t * availableLength)
{
	uint8_t * command_lengthPosition;

	// set header
	tlv_setData((uint8_t *) headerCommandForSam, sizeof(headerCommandForSam), &ptr, availableLength);

	// create sam command
	command_lengthPosition = asn1_openTag1(ASN1_CHOICE_CONSTRUCTED | PAYLOAD_SAM_COMMAND, &ptr, availableLength);
	{
		uint8_t * samCommand_lengthPosition;

		// create get-content-element command
		samCommand_lengthPosition = asn1_openTag1(ASN1_CHOICE_CONSTRUCTED | SAMCOMMAND_GET_CONTENT_ELEMENT, &ptr,
			 availableLength);
		{
			uint8_t elementTag = IMPLICIT_FORMAT_PAC_BITS;

			// set contentElementTag
			tlv_create(ASN1_CLASS_CONTEXT | 0, &elementTag, sizeof(elementTag), &ptr, availableLength);
		}
		asn1_closeTag1(samCommand_lengthPosition, ptr);
	}
	asn1_closeTag1(command_lengthPosition, ptr);
}
#endif 





/// <summary>
/// Process a received hfCommand message
///
/// Low-level callback functions are required to handle the decoded HF commands
/// </summary>
/// <param name="ptr">buffer to hold the message data</param>
/// <param name="payloadLength">length of received message data in the buffer</param>
/// <param name="availableLength">free space remaining in the buffer</param>
uint8_t hfCommand_process(uint8_t * ptr, uint16_t * payloadLength, uint16_t * availableLength)
{
	if (tlv_readTag(&ptr, payloadLength) == (ASN1_CHOICE_CONSTRUCTED | PAYLOAD_HF_COMMAND)) {
		uint16_t commandType;
		uint16_t objectLength;

		tlv_readLength(&ptr, payloadLength);

		commandType = tlv_readTag(&ptr, payloadLength);
		objectLength = tlv_readLength(&ptr, payloadLength);

		switch (commandType)
		{
			// transmit
			case ASN1_CHOICE_CONSTRUCTED | HFCOMMAND_TRANSMIT:
				{
					uint16_t rxRfStatus = ASN1_RF_STATUS_OK;
					uint8_t * rxData = transferBuffer;
					uint16_t rxDataLen = 0;
					uint8_t * txData;
					uint16_t txDataLen;
					uint16_t txFlags = ASN1_RF_FLAGS_NONE;
					uint16_t len;
					uint32_t timeout = 200;
					// get the data
					txDataLen = tlv_search(&ptr, &objectLength, ASN1_CLASS_CONTEXT | 0);
					txData = ptr;

					ptr += txDataLen;

					len = tlv_search(&ptr, &objectLength, ASN1_CLASS_CONTEXT | 1);
					ptr += len;

					// process the optional data
					while (objectLength > 0) {
						uint16_t tag;
						uint16_t optionalObjectLength;

						tag = tlv_readTag(&ptr, &objectLength);
						optionalObjectLength = tlv_readLength(&ptr, &objectLength);

						switch (tag)
						{
							//protocol
							case ASN1_CLASS_CONTEXT | 1:{
								uint8_t protocol = ptr[1];
								HID_RF_SetFrameProtocol(protocol);
							}
							break;
							
							// timeOut
							case ASN1_CLASS_CONTEXT | 2:
								//timeOut = asn1_integerAsU4(ptr, optionalObjectLength);
								if(optionalObjectLength == 2){
									timeout = utils_getU2(ptr);
								}
								else{
									timeout = utils_getU4(ptr);
								}
								break;

							//DataRateOut
							case ASN1_CLASS_CONTEXT | 3:{
								uint8_t rate = ptr[0];
								HID_RF_SetDataRateOut(rate);
							}
							break;
							
							//DataRateIn
							case ASN1_CLASS_CONTEXT | 4:{
								uint8_t rate = ptr[0];
								HID_RF_SetDataRateIn(rate);
							}
							break;
							
							// dataFormat
							case ASN1_CLASS_CONTEXT | 5:
								// check length is consistent
								if (optionalObjectLength != 3) {
									return(TLV_MALFORMED);
								}

								// get the txFlags
								txFlags = utils_getU2(ptr);
								HID_RF_SetDataFormatFlags(txFlags, ptr[2]);
								// optionally process the txLast byte
								break;

							// all other optional tags ignored in this example
							default:
								break;
						}

						ptr += optionalObjectLength;
						objectLength -= optionalObjectLength;
					}

					// transmit the data to the RF front-end and wait for a response
#if 0
					rxRfStatus = hfCommand_handle(txData, (uint8_t) txDataLen, rxData, &rxDataLen, timecontrol,
						 timereload);
#endif 
					rxRfStatus = HID_RF_Transceive(txData, txDataLen, rxData, &rxDataLen, timeout);
					ptr = workBuffer;
					*availableLength = sizeof(workBuffer);
					hfResponse_rx(ptr, availableLength, rxData, rxDataLen, rxRfStatus);

					break;

				}

				// set field off

			case ASN1_CHOICE_PRIMITIVE | HFCOMMAND_SET_FIELD_OFF:
				{
					HID_RF_Deselect();
					LOG_W("Field off!");
					rf_setField(RFOFF);
					ptr = workBuffer;
					*availableLength = sizeof(workBuffer);

					// prepare the HF Node acknowledge response for sending back to the SAM
					hfResponse_acknowledge(ptr, availableLength);
					break;
				}

			default:
				return(NOT_SUPPORTED);
		}
	}
	else {
		return(TLV_MALFORMED);
	}
	return _TRUE;
}

/// <summary>
/// Prepare an hfResponse_rx message
/// </summary>
/// <param name="ptr">buffer to hold the message data</param>
/// <param name="availableLength">free space remaining in the buffer</param>
/// <param name="rxData">buffer holding the received data</param>
/// <param name="rxDataLen">length of received data in the buffer</param>
/// <param name="rxRfStatus">status of the last rf tranceive</param>

#if 1
void hfResponse_rx(uint8_t * ptr, uint16_t * availableLength, uint8_t * rxData, uint16_t rxDataLen,
	 uint16_t rxRfStatus)
{
	uint8_t * command_lengthPosition;
	uint8_t messageHeader[6];
	uint8_t rfStatus[2];
	uint8_t lengthSize;

	if(rxDataLen < 128){
		lengthSize = 1;
	}
	else{
		lengthSize = 3;
	}
	
	// set up response message header
	messageHeader[0] = IPC_NODEID_HF_INTERFACE;
	messageHeader[1] = IPC_NODEID_SAM;
	messageHeader[2] = IPC_NODEID_NULL;
	messageHeader[3] = MAX_PRIORITY;
	messageHeader[4] = 0;
	messageHeader[5] = 0;

	// set header
	tlv_setData(messageHeader, sizeof(messageHeader), &ptr, availableLength);

	// create response command
	command_lengthPosition = asn1_openTag2(ASN1_CHOICE_CONSTRUCTED | PAYLOAD_RESPONSE, lengthSize, &ptr, availableLength);
	{
		uint8_t * commandClass_lengthPosition;

		// create rf response
		commandClass_lengthPosition = asn1_openTag2(ASN1_CHOICE_CONSTRUCTED | RESPONSE_HFCOMMAND, lengthSize, &ptr,
			 availableLength);
		{
			uint8_t * commandType_lengthPosition;

			// create selected-card-info rf response
			commandType_lengthPosition = asn1_openTag2(ASN1_CHOICE_CONSTRUCTED | RESPONSE_HFCOMMAND_RX, lengthSize, &ptr,
				 availableLength);
			{
				// set rx data
				tlv_create(ASN1_CLASS_CONTEXT | HFCOMMAND_RESPONSE_RX_DATA, rxData, rxDataLen, &ptr, availableLength);

				// set rfStatus
				utils_setU2(rfStatus, rxRfStatus);
				tlv_create(ASN1_CLASS_CONTEXT | HFCOMMAND_RESPONSE_RX_RFSTATUS, rfStatus, sizeof(rfStatus), &ptr,
					 availableLength);
#if 0
				uint8_t bitNum = 0;
				tlv_create(ASN1_CLASS_CONTEXT | HFCOMMAND_RESPONSE_RX_NUM_BITS, &bitNum, sizeof(bitNum), &ptr,
					 availableLength);
#endif 
			}
			asn1_closeTag2(lengthSize, commandType_lengthPosition, ptr);
		}
		asn1_closeTag2(lengthSize, commandClass_lengthPosition, ptr);
	}
	asn1_closeTag2(lengthSize, command_lengthPosition, ptr);
}

#else
void hfResponse_rx(uint8_t * ptr, uint16_t * availableLength, uint8_t * rxData, uint16_t rxDataLen,
	 uint16_t rxRfStatus)
{
	uint8_t * command_lengthPosition;
	uint8_t messageHeader[6];
	uint8_t rfStatus[2];

	// set up response message header
	messageHeader[0] = IPC_NODEID_HF_INTERFACE;
	messageHeader[1] = IPC_NODEID_SAM;
	messageHeader[2] = IPC_NODEID_NULL;
	messageHeader[3] = MAX_PRIORITY;
	messageHeader[4] = 0;
	messageHeader[5] = 0;

	// set header
	tlv_setData(messageHeader, sizeof(messageHeader), &ptr, availableLength);

	// create response command
	command_lengthPosition = asn1_openTag1(ASN1_CHOICE_CONSTRUCTED | PAYLOAD_RESPONSE, &ptr, availableLength);
	{
		uint8_t * commandClass_lengthPosition;

		// create rf response
		commandClass_lengthPosition = asn1_openTag1(ASN1_CHOICE_CONSTRUCTED | RESPONSE_HFCOMMAND, &ptr,
			 availableLength);
		{
			uint8_t * commandType_lengthPosition;

			// create selected-card-info rf response
			commandType_lengthPosition = asn1_openTag1(ASN1_CHOICE_CONSTRUCTED | RESPONSE_HFCOMMAND_RX, &ptr,
				 availableLength);
			{
				// set rx data
				tlv_create(ASN1_CLASS_CONTEXT | HFCOMMAND_RESPONSE_RX_DATA, rxData, rxDataLen, &ptr, availableLength);

				// set rfStatus
				utils_setU2(rfStatus, rxRfStatus);
				tlv_create(ASN1_CLASS_CONTEXT | HFCOMMAND_RESPONSE_RX_RFSTATUS, rfStatus, sizeof(rfStatus), &ptr,
					 availableLength);
#if 0
				uint8_t bitNum = 0;
				tlv_create(ASN1_CLASS_CONTEXT | HFCOMMAND_RESPONSE_RX_NUM_BITS, &bitNum, sizeof(bitNum), &ptr,
					 availableLength);
#endif 
			}
			asn1_closeTag1(commandType_lengthPosition, ptr);
		}
		asn1_closeTag1(commandClass_lengthPosition, ptr);
	}
	asn1_closeTag1(command_lengthPosition, ptr);
}
#endif 


/// <summary>
/// Prepare an hfResponse_acknowledge message
/// </summary>
/// <param name="ptr">buffer to hold the message data</param>
/// <param name="availableLength">free space remaining in the buffer</param>
void hfResponse_acknowledge(uint8_t * ptr, uint16_t * availableLength)
{
	uint8_t * command_lengthPosition;
	uint8_t messageHeader[6];

	// set up response message header
	messageHeader[0] = IPC_NODEID_HF_INTERFACE;
	messageHeader[1] = IPC_NODEID_SAM;
	messageHeader[2] = IPC_NODEID_NULL;
	messageHeader[3] = MAX_PRIORITY;
	messageHeader[4] = 0;
	messageHeader[5] = 0;

	// set header
	tlv_setData(messageHeader, sizeof(messageHeader), &ptr, availableLength);

	// create response command
	command_lengthPosition = asn1_openTag1(ASN1_CHOICE_CONSTRUCTED | PAYLOAD_RESPONSE, &ptr, availableLength);
	{
		uint8_t * commandClass_lengthPosition;

		// create rf response
		commandClass_lengthPosition = asn1_openTag1(ASN1_CHOICE_CONSTRUCTED | RESPONSE_HFCOMMAND, &ptr,
			 availableLength);
		{
			// create empty acknowledge rf response
			tlv_create(ASN1_CHOICE_PRIMITIVE | RESPONSE_HFCOMMAND_ACK, NULL, 0, &ptr, availableLength);
		}
		asn1_closeTag1(commandClass_lengthPosition, ptr);
	}
	asn1_closeTag1(command_lengthPosition, ptr);
}



/// <summary>
/// Process a received bleCommand message
///
/// 
/// </summary>
/// <param name="ptr">buffer to hold the message data</param>
/// <param name="payloadLength">length of received message data in the buffer</param>
/// <param name="availableLength">free space remaining in the buffer</param>
uint8_t bleCommand_process(uint8_t * ptr, uint16_t * payloadLength, uint16_t * availableLength)
{
	if (tlv_readTag(&ptr, payloadLength) == (ASN1_CHOICE_CONSTRUCTED | PAYLOAD_BLE_COMMAND)) {
		uint16_t commandType;
		uint16_t objectLength;

		tlv_readLength(&ptr, payloadLength);

		commandType = tlv_readTag(&ptr, payloadLength);
		objectLength = tlv_readLength(&ptr, payloadLength);

		switch (commandType)
		{
			// transmit
			case ASN1_CHOICE_CONSTRUCTED | BLECOMMAND_TRANSMIT:
				{
					uint16_t rxRfStatus = ASN1_RF_STATUS_OK;
					uint8_t * rxData = transferBuffer;
					uint16_t rxDataLen = 0;
					uint8_t * txData;
					uint16_t txDataLen;
					uint32_t timeout = 2000;
					// get the data
					txDataLen = tlv_search(&ptr, &objectLength, ASN1_CLASS_CONTEXT | 0);
					txData = ptr;

					// transmit the data to the RF front-end and wait for a response
//						rxRfStatus = hid_ble_tranceive(txData, txDataLen, rxData, &rxDataLen, timeout);
					ptr = workBuffer;
					*availableLength = sizeof(workBuffer);
					bleResponse_rx(ptr, availableLength, rxData, rxDataLen);
				}
				break;
				// get version

			case ASN1_CHOICE_PRIMITIVE | BLECOMMAND_GET_VERSION_2:
				{
					ptr = workBuffer;
					*availableLength = sizeof(workBuffer);

					// prepare the HF Node acknowledge response for sending back to the SAM
//						hfResponse_acknowledge(ptr, availableLength);
				}
			break;
			default:{
				LOG_E("Not support command: %02X", commandType);
				return(NOT_SUPPORTED);
			}
		}
	}
	else {
		LOG_E("Not a BLE command!");
		return(TLV_MALFORMED);
	}
	return _TRUE;
}

void bleResponse_rx(uint8_t * ptr, uint16_t * availableLength, uint8_t * rxData, uint16_t rxDataLen)
{
	uint8_t * command_lengthPosition;
	uint8_t messageHeader[6];
	uint8_t rfStatus[2];
	uint8_t lengthSize;

	if(rxDataLen < 128){
		lengthSize = 1;
	}
	else{
		lengthSize = 3;
	}
	
	// set up response message header
	messageHeader[0] = IPC_NODEID_BLE;
	messageHeader[1] = IPC_NODEID_SAM;
	messageHeader[2] = IPC_NODEID_NULL;
	messageHeader[3] = MAX_PRIORITY;
	messageHeader[4] = 0;
	messageHeader[5] = 0;

	// set header
	tlv_setData(messageHeader, sizeof(messageHeader), &ptr, availableLength);

	// create response command
	command_lengthPosition = asn1_openTag2(ASN1_CHOICE_CONSTRUCTED | PAYLOAD_RESPONSE, lengthSize, &ptr, availableLength);
	{
		uint8_t * commandClass_lengthPosition;

		// create rf response
		commandClass_lengthPosition = asn1_openTag2(ASN1_CHOICE_CONSTRUCTED | RESPONSE_BLECOMMAND, lengthSize, &ptr,
			 availableLength);
		{
			uint8_t * commandType_lengthPosition;

			// create selected-card-info rf response
			commandType_lengthPosition = asn1_openTag2(ASN1_CHOICE_CONSTRUCTED | RESPONSE_BLECOMMAND_RX, lengthSize, &ptr,
				 availableLength);
			{
				// set rx data
				tlv_create(ASN1_CLASS_CONTEXT | 0, rxData, rxDataLen, &ptr, availableLength);
			}
			asn1_closeTag2(lengthSize, commandType_lengthPosition, ptr);
		}
		asn1_closeTag2(lengthSize, commandClass_lengthPosition, ptr);
	}
	asn1_closeTag2(lengthSize, command_lengthPosition, ptr);
}


